#ifndef _IPC_API_H_
#define _IPC_API_H_

/* ---------- 二选一配置 ---------- */

/* zmq 需要添加依赖项: -DIPCAPI_USE_ZMQ=1 -lzmq -lrt */
/* nanomsg 需要添加依赖项: -DIPCAPI_USE_NANOMSG=1 -lnanomsg -lanl -lrt -lnsl */
/* (windows版本只要添加上面的宏即可) */

/* ---------- 二选一配置 ---------- */

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* 数据发送/接收缓冲区大小 */
#define IPCAPI_BUFF_SIZE (100*1024)

/*
 *  接收回调函数
 *  函数原型:
 *  void IpcApiCallback(void* handle, void* priv, char* buff, int32_t buffLen)
 *  参数:
 *      priv: 私有指针
 *      index: 可用与数据发收的唯一序号
 */
typedef void (*IpcApiCallback)(void*, void*, char*, int32_t);

typedef enum {
    IPCAPI_TYPE_NONE = 0,
    /* 请求/回复模式 */
    IPCAPI_TYPE_REQREP,
    /* 推送/订阅模式(服务器只发,客户端只收) */
    IPCAPI_TYPE_PUBSUB,
} IpcApiType;

/*
 *  服务器注册
 *  参数:
 *      addr: 地址示例:
 *          "ipc:///tmp/reqrep"  这种写法要求路径/tmp能够创建reqrep文件
 *          "tcp://localhost:5555"
 *      type: 工作模式
 *      priv: 私有指针,回调函数用,不使用则置NULL
 *      callback: 注册接收回调,不使用则置NULL
 *  返回: handle指针
 */
void* IpcApiServer(const char* addr, IpcApiType type, void* priv, IpcApiCallback callback);
void* IpcApiClient(const char* addr, IpcApiType type, void* priv, IpcApiCallback callback);

/* 连接注销 */
void IpcApiRelease(void* handle);

/*
 *  数据发收
 *  参数:
 *      timeout: 0非阻塞,大于0超时ms
 *  返回: 实际发/收字节数
 */
int32_t IpcApiSend(void* handle, const void* buff, int32_t buffLen, int32_t timeout);
int32_t IpcApiRecv(void* handle, void* buff, int32_t buffLen, int32_t timeout);

/*
 *  设置订阅
 *  参数:
 *      topic: 指定开头的内容,为""时表示订阅所有内容
 */
int32_t IpcApiSetTopic(void* handle, const char* topic);

/*
 *  url地址有两种方式:
 *      tcp://127.0.0.1:5555
 *      ipc:///tmp/reqrep
 *  使用后者时需要实际创建/tmp/reqrep文件来挂靠,以下接口用于自动创建该url
 *  以下方法使用"临时路径+pid+clock"生成唯一的url地址,append用于补充功能名称等
 *  示例:
 *      IpcApiCreateUrl(IPCAPI_TYPE_REQREP,buff,sizeof(buff),"test");
 *  生成url:
 *      ipc:///tmp/ipc-reqrep-1234(进程的pid)-1234(clock)-test
 */
void IpcApiCreateUrl(IpcApiType type, char* buff, int32_t buffSize, const char* append);

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
/* -------------------- c++ 封装 -------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

class CIpcApiServer
{
public:
    CIpcApiServer()
    {
        mHandle = NULL;
        memset(mAddr, 0, sizeof(mAddr));
        mType = 0;
        mRspBufferCache = (char*)calloc(1, IPCAPI_BUFF_SIZE);
    }

    ~CIpcApiServer()
    {
        Release();
    }

    int32_t Init(const char* addr, int32_t type)
    {
        if (mHandle) {
            IpcApiRelease(mHandle);
            mHandle = NULL;
        }
        snprintf(mAddr, sizeof(mAddr), "%s", addr);
        mType = type;
        mRspBufferCache = (char*)calloc(1, IPCAPI_BUFF_SIZE);
        /* create */
        mHandle = IpcApiServer(mAddr, (IpcApiType)mType, (void*)this, CIpcApiServer::OnRequestPrivate);
        if (!mHandle)
        {
            OnError("IpcApiServer init failed: type:%d addr:%s", mType, mAddr);
            return -1;
        }
        return 0;
    }

    void Release()
    {
        if (mHandle) {
            IpcApiRelease(mHandle);
        }
        mHandle = NULL;
        if (mRspBufferCache) {
            free(mRspBufferCache);
        }
        mRspBufferCache = NULL;
        memset(mAddr, 0, sizeof(mAddr));
        mType = 0;
    }

    /* 仅 IPCAPI_TYPE_REQREP 模式可用 */
    virtual void OnRequest(
        char* reqBuff, int32_t reqBuffSize,
        char* rspBuff, int32_t& rspBuffSize)
    {
        fprintf(stdout, "OnRequest: %s \r\n", reqBuff);
        /* 默认回复相同内容 */
        rspBuffSize = rspBuffSize > reqBuffSize ? reqBuffSize : rspBuffSize;
        memcpy(rspBuff, reqBuff, rspBuffSize);
    }

    /* 仅 IPCAPI_TYPE_PUBSUB 模式可用 */
    int32_t Broadcast(const char* msg, int32_t msgSize)
    {
        /* 尝试重启 */
        if (!mHandle) {
            return -1;
        }
        if (mType != IPCAPI_TYPE_PUBSUB) {
            return -2;
        }
        /* 广播 */
        int32_t ret = IpcApiSend(mHandle, (const void*)msg, msgSize, 0);
        if (ret != msgSize) {
            OnError("Broadcast failed: ret:%d size:%d msg:%s", ret, msgSize, msg);
            return -2;
        }
        return 0;
    }

    /* 异常:输出端 */
    virtual void OnErrorPrint(const char* errMsg) {
        fprintf(stderr, "OnError: %s", errMsg);
    }

    /* 异常:调用端 */
    void OnError(const char* fmt, ...) {
        char buff[1024] = {0};
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(buff, sizeof(buff) - 1, fmt, ap);
        va_end(ap);
        OnErrorPrint(buff);
    }

protected:
    void* mHandle;
    char mAddr[256];
    int32_t mType;
    char* mRspBufferCache;

    static void OnRequestPrivate(void* handle, void* priv, char* reqBuff, int32_t reqBuffSize)
    {
        CIpcApiServer* cias = (CIpcApiServer*)priv;
        /* 请求事件 */
        int32_t rspBuffSize = IPCAPI_BUFF_SIZE;
        char* rspBuff = cias->mRspBufferCache;
        if (!rspBuff) {
            cias->OnError("OnRequestPrivate alloc failed: size:%d", IPCAPI_BUFF_SIZE);
            return;
        }
        cias->OnRequest(reqBuff, reqBuffSize, rspBuff, rspBuffSize);
        /* 回复(每个请求都必须回复) */
        int32_t ret = IpcApiSend(handle, (const void*)rspBuff, rspBuffSize, 0);
        if (ret != rspBuffSize) {
            cias->OnError("IpcApiSend failed: ret:%d size:%d msg:%s", ret, rspBuffSize, rspBuff);
        }
    }
};

class CIpcApiClient
{
public:
    CIpcApiClient()
    {
        mHandle = NULL;
        memset(mAddr, 0, sizeof(mAddr));
        mType = 0;
    }

    ~CIpcApiClient()
    {
        Release();
    }

    int32_t Init(const char* addr, int32_t type)
    {
        Release();
        snprintf(mAddr, sizeof(mAddr), "%s", addr);
        mType = type;
        /* connect */
        if (mHandle) {
            IpcApiRelease(mHandle);
            mHandle = NULL;
        }
        mHandle = IpcApiClient(mAddr, (IpcApiType)mType, (void*)this, mType == IPCAPI_TYPE_PUBSUB ? CIpcApiClient::OnMessagePrivate : NULL);
        if (!mHandle)
        {
            OnError("IpcApiClient create failed: type:%d addr:%s", mType, mAddr);
            return -1;
        }
        return 0;
    }

    void Release()
    {
        if (mHandle) {
            IpcApiRelease(mHandle);
        }
        mHandle = NULL;
        memset(mAddr, 0, sizeof(mAddr));
        mType = 0;
    }

    /* 仅 IPCAPI_TYPE_REQREP 模式可用 */
    int32_t Request(
        const char* reqBuff, int32_t reqBuffSize,
        char* rspBuff, int32_t& rspBuffSize,
        int32_t timeout = 1000) /* 默认超时1秒,写0表示非阻塞(不要求回复时使用) */
    {
        /* 尝试重连 */
        if (!mHandle) {
            return -1;
        }
        if (mType != IPCAPI_TYPE_REQREP) {
            return -2;
        }
        /* 请求 */
        int32_t ret = IpcApiSend(mHandle, (const void*)reqBuff, reqBuffSize, timeout);
        if (ret != reqBuffSize) {
            return -2;
        }
        /* 回复 */
        ret = IpcApiRecv(mHandle, (void*)rspBuff, rspBuffSize, timeout);
        if (ret < 0) {
            rspBuffSize = 0;
            return -3;
        }
        rspBuffSize = ret;
        return 0;
    }

    /* 仅 IPCAPI_TYPE_PUBSUB 模式可用 */
    int32_t SetTopic(const char* topic)
    {
        /* 尝试重连 */
        if (!mHandle) {
            return -1;
        }
        if (mType != IPCAPI_TYPE_PUBSUB) {
            return -2;
        }
        return IpcApiSetTopic(mHandle, topic);
    }

    /* 仅 IPCAPI_TYPE_PUBSUB 模式可用 */
    virtual void OnMessage(char* msg, int32_t msgSize)
    {
        fprintf(stdout, "OnMessage: %s \r\n", msg);
    }

    /* 异常:输出端 */
    virtual void OnErrorPrint(const char* errMsg) {
        fprintf(stderr, "OnError: %s \r\n", errMsg);
    }

    /* 异常:调用端 */
    void OnError(const char* fmt, ...) {
        char buff[1024] = {0};
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(buff, sizeof(buff) - 1, fmt, ap);
        va_end(ap);
        OnErrorPrint(buff);
    }

protected:
    void* mHandle;
    char mAddr[256];
    int32_t mType;

    static void OnMessagePrivate(void* handle, void* priv, char* msg, int32_t msgSize)
    {
        CIpcApiClient* ciac = (CIpcApiClient*)priv;
        ciac->OnMessage(msg, msgSize);
    }
};

/* -------------------- c++ 封装 -------------------- */
#endif

#endif // _IPC_API_H_
